// Copyright 2010-2012, Vanya Davidenko.
// Используемая кодировка: UTF-8.

#ifndef LIBV_LOCK_SMART_H_
#define LIBV_LOCK_SMART_H_

#include <algorithm>  // ::std::swap

#include <libv/check.h>
#include <libv/lock.h>


namespace libv
{

// Объявление типов-тегов.
typedef bool IsAlreadyLockedLock;
typedef int TryToLock;


/** Класс для RAII-захвата блокировок со схожей с ::std::auto_ptr
  * семантикой владения.*/
class SmartLock
{
  public:
    /** Класс получает указатель на блокировку и блокирует ее.
      * @pre resource != NULL. */
    explicit SmartLock(LockableResourceInterface* resource)
        : resource_(resource),
          is_locked_(false)
    {
        LIBV_PRECOND(resource != NULL);
        this->Lock();
    }


    /** Класс принимает во владение уже заблокированную блокировку.
      * @pre resource != NULL. */
    SmartLock(LockableResourceInterface* resource, IsAlreadyLockedLock)
        : resource_(resource),
          is_locked_(true)
    {
        LIBV_PRECOND(resource != NULL);
    }


    /** Класс получает указатель на блокировку и производит одну попытку ее
      * блокирования. Проверка успешности осуществляется методом owns_lock().
      * В случае неуспеха класс продолжает ссылаться на блокировку.
      * Методы TryLock() или Lock() могут быть вызваны повторно.
      * @pre resource != NULL. */
    SmartLock(LockableResourceInterface* resource, TryToLock)
        : resource_(resource),
          is_locked_(false)
    {
        LIBV_PRECOND(resource != NULL);
        this->TryLock();
    }


    /** Конструктор копирования забирает у переданного объекта владение
      * блокировкой, но оставляет ссылку на нее, по которой блокировка может
      * быть получена повторно через методы TryLock и Lock. */
    SmartLock(SmartLock& rhv)
        : resource_(rhv.resource_),
          is_locked_(rhv.is_locked_)
    {
        rhv.is_locked_ = false;
    }


    ~SmartLock()
    {
        if ( is_locked() )
            this->Unlock();
    }


    void Swap(SmartLock& rhv)
    {
        ::std::swap(resource_, rhv.resource_);
        ::std::swap(is_locked_, rhv.is_locked_);
    }


    /** Оператор присваивания забирает у переданного объекта владение
      * блокировкой, но оставляет ссылку на нее. */
    SmartLock& operator=(SmartLock& rhv)
    {
        SmartLock tmp(rhv);
        this->Swap(tmp);
        return *this;
    }


    /** Метод возвращает указатель на блокировку, не разблокировав ее. */
    LockableResourceInterface* Get() const
    {
        return resource_;
    }


    /** Попытка взятия блокировки,
      * на которую ссылается, но которой не владеет данный объект.
      * @pre owns_lock() == false. */
    bool TryLock()
    {
        LIBV_PRECOND(is_locked() == false);

        is_locked_ = resource_->TryLock();
        return is_locked_;
    }


    /** Взятие блокировки, на которую ссылается, но не владеет данный объект.
      * @pre owns_lock() == false. */
    void Lock()
    {
        LIBV_PRECOND(is_locked() == false);

        resource_->Lock();
        is_locked_ = true;
    }


    /** Разблокирование блокировки, которой владеет объект.
      * Объект продолжает ссылаться на блокировку.
      * @pre is_locked(). */
    void Unlock()
    {
        LIBV_PRECOND(is_locked());

        resource_->Unlock();
        is_locked_ = false;
    }


    /** Проверка условия, владения блокировкой. */
    bool is_locked() const
    {
        return is_locked_;
    }


  private:
    LockableResourceInterface* resource_;
    bool is_locked_;
};

}  // libv

#endif  // LIBV_LOCK_SMART_H_
